#include "unp.h"
#include "unpifi.h"
#include <linux/if_ether.h>

/* Ethernet header are are always exactly 14 bytes */
#define SIZE_ETHERNET	14

/* Ethernet address are 6 bytes */
#define ETHER_ADDR_LEN	6

#define LINE_WIDTH		16

/*
+---------+---------+---------------------------------------------------+
| Version | Header  | Type of Service |           Total Length          |
|         |  Length |                 |                                 |
| (4bit)  | (4bit)  |   (8bit)        |             (16bit)             |
+---------+---------+-----------------+--------+------------------------+
|           Identification            |  Flag  |     Fragment Offset    |
|                                     |        |                        |
|               (16bit)               | (3bit) |        (13bit)         |
+-------------------+-----------------+--------+------------------------+
|   Time to Live    |    Protocol     |         Header Checksum         |
|                   |                 |                                 |
|      (8bit)       |     (8bit)      |            (16bit)              |
+-------------------+-----------------+---------------------------------+
|                              Source IP Address                        |
|                                                                       |
|                                  (32bit)                              |
------------------------------------------------------------------------+
|                           Destination IP Address                      |
|                                                                       |
|                                  (32bit)                              |
+-----------------------------------------------------------------------+
|                                 Options                               |
|                                                                       |
|                                  (32bit)                              |
+-----------------------------------------------------------------------+
|                                  Data                                 |
|                                                                       |
|                                  ...                                  |
+-----------------------------------------------------------------------+
*/


struct sniff_ip {
	u_char			ip_vhl;					/* version &0xf0 >> 4 | header len &0x0f*/
    unsigned char   ip_tos;					/* type of service*/
    unsigned short  ip_len;					/* total length */
    unsigned short  ip_id;					/* Identification */
    unsigned short  ip_off;					/* flag offset field */
	#define  IP_REF	0x800					/* reserved flagment flag */
	#define  IP_DF	0x400					/* dont fragment flag */
	#define  IP_MF	0x200					/* more fragment flag */
	#define  IP_OFFMASK	0x1fff				/* mask for fragment bits */
    unsigned char   ip_ttl;					/* time to live */				
    unsigned char   ip_protocol;			/* protocol */
    unsigned short  ip_cksum;				/* checksum */
    struct in_addr  ip_src;					/* source address */
    struct in_addr	ip_dst;					/* destination address */
};

#define IP_HL(ip)	((ip->ip_vhl) & 0x0f)
#define IP_V(ip)	(((ip->ip_vhl) & 0xf0) >> 4)

/*
Ehernet II帧结构：
帧头：6个字节的目的MAC地址和6字节的源MAC地址。2字节的类型字段，表示封装在数据中的数据类型。
数据：46-1500字节的数据字段。
帧尾 ：4字节的帧效验序列。

+-------+--------------+----------------------------------------+------+
| D-MAC | S-MAC | TYPE |                    DATA                | CRC  |
| 6B    |  6B   | 2B   |                                        |  4B  |
+-------+--------------+----------------------------------------+------+ 

*/

struct sniff_ethernet {
	u_char	ether_dhost[ETHER_ADDR_LEN];	/* destination host address */
	u_char	ether_shost[ETHER_ADDR_LEN];	/* source host address */
	u_short ether_type;						/* IP? ARP? RARP? etc */
};

/* 
 TCP 格式
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               |                               |
|       Source Port             |        Destination Port       |
|                               |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                        Sequence Number                        |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                    Acknowledgment NUmber                      |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data |            |U|A|P|R|S|F|                               |
|offset|  Reserved  |R|C|S|S|Y|I|        Window                 |
|      |            |G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               |                               |
|       Checksum                |          Urgent Pointer       |
|                               |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                               |               |
|                  Options                      |   Padding     |
|                                               |               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                             Data                              |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

typedef u_int tcp_seq;
struct sniff_tcp {
	u_short	th_sport;				/* source port */
	u_short th_dport;				/* destination port */
	tcp_seq	th_seq;					/* sequence number */
	tcp_seq th_ack;					/* acknowledgment number */
	u_char  th_offx2;				/* data offset, reserved , 因为保留位全为０，被拆成两部分*/
	#define TH_OFF(th) (((th->th_offx2) & 0xf0) >> 4) 
	u_char	th_flags;				/* flags */
	#define TH_FIN	0x01 
	#define TH_SYN	0x02 
	#define TH_RST	0x04 
	#define TH_PUSH	0x08 
	#define TH_ACK	0x10 
	#define TH_URG  0x20
	#define TH_ECE	0x40
	#define TH_CWR	0x80
	#define TH_FLAGS	(TH_FIN | TH_SYN | TH_RST | TH_ACK | TH_URG | TH_ECE | TH_CWR) 
	u_short	th_win;					/* window */
	u_short th_sum;					/* checksum */
	u_short th_urp;					/* urgent pointer */
};




void print_host_info() {
	int					i;
	struct sockaddr		*sa;
	struct ifi_info		*ifi, *ifihead;
	u_char				*ptr;

	//本机接口信息
	for (ifihead = ifi = get_ifi_info(AF_INET, 1);
			ifi != NULL; ifi = ifi->ifi_next) {
		printf("%s:", ifi->ifi_name);
		if (ifi->ifi_index !=  0)
			printf("%d", ifi->ifi_index);
		printf("\n");
		if ((i = ifi->ifi_hlen) > 0) {
			ptr = ifi->ifi_haddr;
			printf("\tMac: ");
			do {
				printf("%s%.2x", (i == 0) ? "" : ":", *ptr++);
			} while(--i > 0);
			printf("\n");
		}
		//IP
		if ((sa = ifi->ifi_addr) != NULL) {
			printf("\t%s\n", inet_ntoa(((struct sockaddr_in *)sa)->sin_addr));
		}
	}
	free_ifi_info(ifihead);
}

/*
 * 打印一行(16个字节): offset hex ascii
 *
 * 00000   47 45 54 20 2f 20 48 54  54 50 2f 31 2e 31 0d 0a   GET / HTTP/1.1..
 */
void
print_hex_ascii_line(const u_char *payload,  ssize_t len, ssize_t offset) {
	ssize_t			i;
	int				gap;
	const u_char	*ch;

	/* offset */
	printf("%05zd\t", offset);

	/* hex */
	ch = payload;
	for (i = 0; i < len; ++i) {
		printf("%02x ", *ch++);
		if (i == LINE_WIDTH / 2 - 1) 
			printf("%2c ", ' ');
	}
	if (len < LINE_WIDTH / 2) 
		printf("%2x ", ' ');	/* 不够８个字节时打印空格 */
	if (len < LINE_WIDTH) {
		gap = LINE_WIDTH - len;
		while(gap-- > 0) 
			printf("%2c ", ' ');
	}
	printf("\t");

	/* ascii */
	ch = payload;
	for (i = 0; i < len; ++i) {
		if (isprint(*ch))
			printf("%c", *ch);
		else
			printf("%c", '.');
		++ch;
	}
	printf("\n");
}

/*
 * 打印有效载荷(避免打印二进制)
 */
void
print_payload(const u_char *payload, ssize_t len) {
	ssize_t			len_rem = len;
	ssize_t			offset	= 0;
	const u_char	*ptr = payload;

	if (len_rem == 0) {
		return;
	}

	/* 只有一行 */
	if (len <= LINE_WIDTH) {
		print_hex_ascii_line(ptr, len, offset);
		return;
	}

	/* data跨越多行 */
	for ( ; ; ) {
		if (len_rem <= LINE_WIDTH) {
			print_hex_ascii_line(ptr, len_rem, offset);
			break;
		}
		print_hex_ascii_line(ptr, LINE_WIDTH, offset);
		len_rem -= LINE_WIDTH;
		ptr += LINE_WIDTH;
		offset += LINE_WIDTH;
	}
}

void
got_packet(const u_char *packet) {
	static int count			= 1;		/* packet counter */

	/* 声明指向分组的指针 */
	struct sniff_ethernet		*ethernet;
	struct sniff_tcp			*tcp;
	struct sniff_ip				*ip;
	const u_char				*p, *payload;

	ssize_t size_ip;
	ssize_t size_tcp;
	ssize_t size_payload;			/* 有效载荷 */

	printf("\nPacket number %d:\n", count++);

	printf("\t源主机\t\t\t目标主机\n");
	ethernet = (struct sniff_ethernet *)packet;
	p = ethernet->ether_shost;
	printf("\t%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
				p[6] & 0xff, p[7] & 0xff,p[8] & 0xff, p[9] & 0xff,p[10] & 0xff, p[11] & 0xff);
	p = ethernet->ether_dhost;
	printf("\t%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
			p[0] & 0xff, p[1] & 0xff,p[2] & 0xff, p[3] & 0xff, p[4] & 0xff, p[5] & 0xff);

	ip = (struct sniff_ip *)(packet + SIZE_ETHERNET);
	size_ip = IP_HL(ip) * 4;
	if (size_ip < 20) {
		printf(" Invalid IP header length: %zu\n", size_ip);
		return;
	}

	printf("\tFrom:%s", inet_ntoa(ip->ip_src));
	printf("\tTo:%s\n", inet_ntoa(ip->ip_dst));

	switch(ip->ip_protocol) {
		case IPPROTO_TCP:
			printf("\tProtocl: TCP\n");
			break;
		case IPPROTO_UDP:
			printf("\tProtocl: UDP\n");
			return;
		case IPPROTO_ICMP:
			printf("\tProtocl: ICMP\n");
			return;
		case IPPROTO_IP:
			printf("\tProtocl: IP\n");
			return;
		case IPPROTO_RAW:
			printf("\tProtocl: RAW\n");
			return;
		default:
			printf("\tProtocl: Unknown\n");
			return;
	}

	/*
	 * 只处理tcp
	 */

	/* 定义和计算tcp head偏移 */
	tcp = (struct sniff_tcp *)(packet + SIZE_ETHERNET + size_ip);
	size_tcp = TH_OFF(tcp) * 4;
	if (size_tcp < 20) {
		printf("\tInvalid TCP header length:%zu bytes\n", size_tcp);
		return;
	}
	
	printf("\t源端口: %d\t\t目标端口:%d\n", tcp->th_sport, tcp->th_dport);

	/* 定义/计算有效载荷段偏移 */
	payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp); 

	/* 计算tcp 载荷大小 */
	size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);

	/*
	 * 打印有效载荷的长度
	 */
	if (size_payload > 0) {
		printf("\tPayload (%zu bytes): \n", size_payload);
		print_payload(payload, size_payload);
	}
}

/*
* PF_PACKET协议簇可以让一个应用程序把数据包变成似乎从网络层接收的样子
* 但是没有办法抓到那些不是发向自己主机的包。
* 正如我们前面看到的，网卡丢弃所有不含有主机MAC地址的数据包
* 这是因为网卡处于非混杂模式，即每个网卡只处理源地址是它自己的帧！
* 只有三个例外：
* 			如果一个帧的目的MAC地址是一个受限的广播地址（255.255.255.255)那么它将被所有的网卡接收：
* 			如果一个帧的目的地址是组播地址，那么它将被那些打开组播接收功能的网卡所接收；
* 			网卡如被设置成混杂模式，那么它将接收所有流经它的数据包
*/

int
main() {
    int                 sockfd;
    ssize_t             n;
    u_char              buf[MAXLINE];
    //struct sockaddr_in  servaddr;

    if ((sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0) {
        perror("create sockt");
        exit(EXIT_FAILURE);
    }

	/*
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);

	setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, (char *)&flag, sizeof(flag));	//设置IP头操作选项
	if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) 
		err_sys("bind error");
	*/
	
	print_host_info();		/* 打印本机接口信息 */

	//抓包
    for ( ; ; ) {
        n = recvfrom(sockfd, buf, MAXLINE, 0, 0, 0);


        /*******************************************
         ** 14   6(dest)+6(source)+2(type or length)
         ** +
         ** 20   ip header 
         ** +
         ** 8    icmp,tcp or udp header
         ** = 42
         *********************************************/

        if (n < 42) {
               fprintf(stdout, "Incomplete header, packet corrupt.\n"); 
               continue;
        }

		got_packet(buf);
	}

	return 0;
}
